package org.xukai.remoting.http;

import okhttp3.Credentials;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.http.HttpHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class HTTPClient {
    private static final Logger LOG = LoggerFactory.getLogger(HTTPClient.class);
    private OkHttpClient client;

    public HTTPClient() {
        this.buildClient(180, 180, 180);
    }

    private void buildClient(long readTimeout, long writeTimeout, long connectTimeout) {
        this.client = new OkHttpClient.Builder()
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .build();
    }

    public String buildAuth(HttpBasicAuth auth) {
        if (auth == null) {
            return null;
        }
        return buildAuth(auth.getUsername(), auth.getPassword());
    }

    public String buildAuth(String username, String password) {
        return Credentials.basic(username, password);
    }

    private Request.Builder buildRequest(Map<String, String> apiAuthHeaderMap) {
        Request.Builder requestBuilder = new Request.Builder().header(HttpHeaders.USER_AGENT, "strange")
                .header(HttpHeaders.CONTENT_TYPE, "application/json");

        apiAuthHeaderMap.forEach(requestBuilder::addHeader);

        return requestBuilder;
    }

    private Request.Builder buildRequest(String auth) {
        Request.Builder requestBuilder = new Request.Builder().header(HttpHeaders.USER_AGENT, "strange")
                .header(HttpHeaders.CONTENT_TYPE, "application/json");
        if (auth != null) {
            requestBuilder = requestBuilder.header(HttpHeaders.AUTHORIZATION, auth);
        }

        return requestBuilder;
    }

    public Request.Builder buildRequest(String auth, String contentType) {
        return new Request.Builder()
                .header("Authorization", auth)
                .header("X-Requested-By", "strange")
                .header("X-Requested-With", "XMLHttpRequest")
                .header("Content-Type",contentType);
    }

    public RequestBody buildRequestBody(String type, String body) {
        body = body == null ? "":body;
        return RequestBody.create(MediaType.parse(type), body);
    }

    public Response execute(Request request) throws IOException {
        return client.newCall(request).execute();
    }

    private void checkResponseSuccess(Response response) throws IOException {
        int code = response.code();
        if (code < 200 || code >= 400) {
            if(response.body() != null) {
                response.close();
                throw new IOException("response body:" + response.body());
            } else {
                response.close();
                throw new IOException("response body is null.");
            }
        }
    }

    public Response get(Map<String, String> apiAuthHeaderMap, String url) throws IOException {
        Request request = buildRequest(apiAuthHeaderMap).url(url).get().build();
        Response response = execute(request);
        checkResponseSuccess(response);
        return response;
    }

    public Response put(Map<String, String> apiAuthHeaderMap, String url, RequestBody requestBody)
            throws IOException {
        Request request = buildRequest(apiAuthHeaderMap).url(url).put(requestBody).build();
        return execute(request);
    }

    public Response post(Map<String, String> apiAuthHeaderMap, String url, RequestBody requestBody)
            throws IOException {
        Request request = buildRequest(apiAuthHeaderMap).url(url).post(requestBody).build();
        return execute(request);
    }

    public Response deleteWithBody(Map<String, String> apiAuthHeaderMap, String url, RequestBody requestBody)
            throws IOException {
        Request request = buildRequest(apiAuthHeaderMap).url(url).delete(requestBody).build();
        return execute(request);
    }

    public Response get(String auth, String url) throws IOException {
        Request request = buildRequest(auth).url(url).get().build();
        Response response = execute(request);
        checkResponseSuccess(response);
        return response;
    }

    public Response put(String auth, String url, RequestBody requestBody) throws IOException {
        Request request = buildRequest(auth).url(url).put(requestBody).build();
        return execute(request);
    }

    public Response post(String auth, String url, RequestBody requestBody) throws IOException {
        Request request = buildRequest(auth).url(url).post(requestBody).build();
        return execute(request);
    }

    public Response post(String auth, String contentType, String url, RequestBody requestBody) throws IOException {
        Request request = buildRequest(auth, contentType).url(url).post(requestBody).build();
        return execute(request);
    }

    public Response deleteWithBody(String auth, String url, RequestBody requestBody) throws IOException {
        Request request = buildRequest(auth).url(url).delete(requestBody).build();
        return execute(request);
    }

    public Response delete(String auth, String url) throws IOException {
        Request request = buildRequest(auth).url(url).delete().build();
        return execute(request);
    }

    public Response delete(Map<String, String> apiAuthHeaderMap, String url) throws IOException {
        Request request = buildRequest(apiAuthHeaderMap).url(url).delete().build();
        return execute(request);
    }

    public String deleteReturnStr(Map<String, String> apiAuthHeaderMap, String url) throws IOException {
        try (Response response = this.delete(apiAuthHeaderMap, url)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[DELETE]" + url);
            throw e;
        }
    }

    public String deleteReturnStr(String auth, String url) throws IOException {
        try (Response response = this.delete(auth, url)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[DELETE]" + url);
            throw e;
        }
    }

    public String deleteWithBodyReturnStr(String auth, String url, RequestBody requestBody) throws IOException {
        try (Response response = this.deleteWithBody(auth, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[DELETE_WITH_BODY]" + url);
            throw e;
        }
    }

    public String deleteWithBodyReturnStr(Map<String, String> headers, String url, RequestBody requestBody)
            throws IOException {
        try (Response response = this.deleteWithBody(headers, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[DELETE_WITH_BODY]" + url);
            throw e;
        }
    }

    public String getReturnStr(String auth, String url) throws IOException {
        try (Response response = this.get(auth, url)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[GET]" + url);
            throw e;
        }
    }

    public String getReturnStr(Map<String, String> headers, String url) throws IOException {
        try (Response response = this.get(headers, url)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[GET]" + url);
            throw e;
        }
    }

    public String putReturnStr(Map<String, String> headers, String url, RequestBody requestBody) throws IOException {
        try (Response response = this.put(headers, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[PUT]" + url);
            throw e;
        }
    }

    public String putReturnStr(String auth, String url, RequestBody requestBody) throws IOException {
        try (Response response = this.put(auth, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[PUT]" + url);
            throw e;
        }
    }

    public String postReturnStr(Map<String, String> headers, String url, RequestBody requestBody) throws IOException {
        try (Response response = this.post(headers, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[POST]" + url);
            throw e;
        }
    }

    public String postReturnStr(Map<String, String> headers, String url, String requestBody) throws IOException {
        RequestBody body = buildRequestBody("application/json", requestBody);
        return postReturnStr(headers, url, body);
    }

    public String postReturnStr(String auth, String url, RequestBody requestBody) throws IOException {
        try (Response response = this.post(auth, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[POST]" + url);
            throw e;
        }
    }

    public String postReturnStr(String auth, String contentType, String url, RequestBody requestBody) throws IOException {
        try (Response response = this.post(auth, contentType, url, requestBody)) {
            assert response.body() != null;
            return response.body().string();
        } catch (IOException e) {
            LOG.error("[POST]" + url);
            throw e;
        }
    }

    public String postReturnStr(String auth, String url, String requestBody) throws IOException {
        RequestBody body = buildRequestBody("application/json", requestBody);
        return postReturnStr(auth, url, body);
    }

    public String putReturnStr(String auth, String url, String requestBody) throws IOException {
        RequestBody body = buildRequestBody("application/json", requestBody);
        return putReturnStr(auth, url, body);
    }

    public String putReturnStr(Map<String, String> headers, String url, String requestBody) throws IOException {
        RequestBody body = buildRequestBody("application/json", requestBody);
        return putReturnStr(headers, url, body);
    }

    public String deleteWithBodyReturnStr(String auth, String url, String requestBody) throws IOException {
        RequestBody body = buildRequestBody("application/json", requestBody);
        return deleteWithBodyReturnStr(auth, url, body);
    }

    public String deleteWithBodyReturnStr(Map<String, String> headers, String url, String requestBody)
            throws IOException {
        RequestBody body = buildRequestBody("application/json", requestBody);
        return deleteWithBodyReturnStr(headers, url, body);
    }
}
